package dosgi.core.hook;

import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.hooks.service.EventListenerHook;
import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;

import dbconnector.impl.ServiceInfo;
import dbconnector.service.IServiceInfo;
import dosgi.core.util.DatabaseProxy;

/**
 * Class defining service which will be called during framework service (register, modify, and unregister service) operations. 
 * 
 * @author Jaroslaw Janik
 */
public class ChangeServiceHook implements EventListenerHook {
	
	private DatabaseProxy databaseProxy;
	private String hostIP;
	
	public ChangeServiceHook(String hostIP, DatabaseProxy databaseProxy) {
		this.hostIP = hostIP;
		this.databaseProxy = databaseProxy;
	}
	
	/**
	 * Method which is called prior to service event delivery when a publishing bundle registers, modifies or unregisters a service. 
	 * This method can filter the listeners which receive the event. 
	 */
	@Override
	public void event(ServiceEvent event, Map<BundleContext, Collection<ListenerInfo>> listeners) {
		
		ServiceReference<?> serviceReference = event.getServiceReference();

	    if (isRemote(serviceReference)) {
			switch (event.getType()) {
				case ServiceEvent.REGISTERED: {
					runRegisterThread(serviceReference);
					
					break;
				}
				case ServiceEvent.UNREGISTERING: {
					runUnregisterThread(serviceReference);
					
				    break;
				}
				case ServiceEvent.MODIFIED: {
					
				    break;
				}
			}
		}
	}
	
	private void runRegisterThread(final ServiceReference<?> serviceReference) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				IServiceInfo serviceInfo = getServiceInfo(serviceReference);
				
				databaseProxy.registerService(serviceReference, serviceInfo);
			}
		}).start();
	}
	
	private void runUnregisterThread(final ServiceReference<?> serviceReference) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				databaseProxy.unregisterService(serviceReference);
			}
		}).start();
	}
	
	private boolean isRemote(ServiceReference<?> serviceReference) {
		String propertyValue = (String) serviceReference.getProperty(HooksManager.REMOTE_PROPERTY_KEY);
	    if (propertyValue != null && propertyValue.equals(HooksManager.REMOTE_PROPERTY_VALUE_TRUE)) {
	    	return true;
	    }
	    
	    return false;
	}
	
	private IServiceInfo getServiceInfo(ServiceReference<?> reference) {
		IServiceInfo serviceInfo = new ServiceInfo();

		serviceInfo.setHost(getHost());
		serviceInfo.setServiceName(getServiceName(reference));
		serviceInfo.setProperties(getProperties(reference));
		
		return serviceInfo;
	}
	
	private String getHost() {
		return hostIP;
	}
	
	private Dictionary<String, String> getProperties(ServiceReference<?> reference) {
		Dictionary<String, String> properties = new Hashtable<String, String>();
		
		String[] keys = reference.getPropertyKeys();
		for (String key : keys) {
			Object value = reference.getProperty(key);
			
			if (value instanceof String) {
				if (!key.equals(HooksManager.REMOTE_PROPERTY_KEY)) {
					properties.put(key, (String) value);
				}
			}
		}
		
		return properties;
	}
	
	private String getServiceName(ServiceReference<?> reference) {
		Pattern pattern = Pattern.compile("\\{([\\w\\.]*)\\}.*");
		
		Matcher matcher = pattern.matcher(reference.toString());
		if (matcher.find()) {
			return matcher.group(1);
		}
		
		return "";
	}
}
